﻿using System.Collections;
using System.Linq;
using System.Linq.Expressions;

using Tszy.Unlimited.Base.Exceptions;
using Tszy.Unlimited.Data.AdoNet.Sql.Components.Expressions.Values;
using Tszy.Unlimited.Data.AdoNet.Sql.Descriptors;
using Tszy.Unlimited.Data.AdoNet.Sql.Providers;

namespace Tszy.Unlimited.Data.AdoNet.Sql.Components.Expressions.Where.Call
{
    internal class CallValueExpressionHandlerHandler : WhereExpressionHandlerBase
    {
        public override WhereExpressionResult Execute(ExpressionContext context, Expression expression)
        {
            var callExpression = expression as MethodCallExpression;

            if (callExpression == null)
                return new WhereExpressionResult { Status = true };

            //表达式中的方法名。
            var methodName = callExpression.Method.Name;

            if (methodName == "Contains")
            {
                var like = BuildLike(context, callExpression, LikeType.Equal);

                return new WhereExpressionResult { Status = true, CallExpressionDescriptor = like };
            }

            if (methodName == "StartsWith")
            {
                var like = BuildLike(context, callExpression, LikeType.StartsWith);

                return new WhereExpressionResult { Status = true, CallExpressionDescriptor = like };
            }

            if (methodName == "EndsWith")
            {
                var like = BuildLike(context, callExpression, LikeType.EndsWith);

                return new WhereExpressionResult { Status = true, CallExpressionDescriptor = like };
            }

            if (methodName == "In")
            {
                var whereIn = BuildIn(context, callExpression);

                return new WhereExpressionResult { Status = true, CallExpressionDescriptor = whereIn };
            }

            if (methodName == "NotIn")
            {
                var whereNotIn = BuildNotIn(context, callExpression);

                return new WhereExpressionResult { Status = true, CallExpressionDescriptor = whereNotIn };
            }

            if (methodName == "Between")
            {
                var whereBetween = BuildBetween(context, callExpression);

                return new WhereExpressionResult { Status = true, CallExpressionDescriptor = whereBetween };
            }

            throw new MainApplicationException("未实现 CALL 转换方法");
        }

        public override ExpressionType GetExpressionType()
        {
            return ExpressionType.Call;
        }

        #region like

        private static IWhereCallExpressionDescriptor BuildLike(
            ExpressionContext context, MethodCallExpression callExpression, LikeType likeType = LikeType.Equal)
        {
            var memberExpression = ValueExpressionHandler.Handler(context, callExpression.Object);

            WhereCallExpressionDescriptor result = null;

            if (memberExpression == null)
                return result;

            //表达式中的方法的参数。
            var methodParameters = callExpression.Arguments;
            //表达式中调用该方法的对象的属性名。
            var parameterName = $"{memberExpression.Value}";

            var methodParameter = methodParameters.FirstOrDefault();
            object currentValue = ValueExpressionHandler.Handler(context, methodParameter).Value;

            result = new WhereCallExpressionDescriptor();

            switch (likeType)
            {
                case LikeType.Equal:
                    //whereBuilder.Append($"{sqlParameterName} LIKE CONCAT('%',{tag}{parameterValue},'%')");
                    //context.ParameterValues[parameterValue] = currentValue;
                    result.Like = new WhereLikeCallExpressionParameter
                    {
                        ParameterName = parameterName,
                        ParameterValue = currentValue,
                        Type = WhereLikeCallExpressionParameterType.Equal,
                    };

                    break;

                case LikeType.StartsWith:
                    //whereBuilder.Append($"{sqlParameterName} LIKE CONCAT({tag}{parameterValue},'%')");
                    //context.ParameterValues[parameterValue] = currentValue;
                    result.Like = new WhereLikeCallExpressionParameter
                    {
                        ParameterName = parameterName,
                        ParameterValue = currentValue,
                        Type = WhereLikeCallExpressionParameterType.StartsWith,
                    };
                    break;

                case LikeType.EndsWith:
                    //whereBuilder.Append($"{sqlParameterName} LIKE CONCAT('%',{tag}{parameterValue})");
                    //context.ParameterValues[parameterValue] = currentValue;
                    result.Like = new WhereLikeCallExpressionParameter
                    {
                        ParameterName = parameterName,
                        ParameterValue = currentValue,
                        Type = WhereLikeCallExpressionParameterType.EndsWith,
                    };
                    break;
            }

            return result;
        }

        private enum LikeType
        {
            Equal = 0,
            StartsWith = 1,
            EndsWith = 2
        }

        #endregion like

        #region in

        private static IWhereCallExpressionDescriptor BuildIn(ExpressionContext context, MethodCallExpression callExpression)
        {
            if (callExpression.Arguments.Count < 2)
                throw new MainApplicationException("获取不到 IN 函数信息");

            var memberExpression = ValueExpressionHandler.Handler(context, callExpression.Arguments[0]);

            if (memberExpression == null)
                throw new MainApplicationException("获取不到 IN 函数");

            var parameterExpression = memberExpression.Expression as ParameterExpression;

            if (parameterExpression == null)
                throw new MainApplicationException("获取不到 IN 函数的参数信息");

            WhereCallExpressionDescriptor result = null;

            var parameterName = $"{memberExpression.Value}";

            //// SQL 参数名。
            //var sqlParameterName = ParameterManager.Instance.GetParameterName(context, parameterExpression, parameterName);

            var valueResult = ValueExpressionHandler.Handler(context, callExpression.Arguments[1]);

            //var whereBuilder = new StringBuilder();

            if (valueResult.Status)
            {
                var fieldList = (IEnumerable)valueResult.Value;

                if (fieldList != null)
                {
                    result = new WhereCallExpressionDescriptor
                    {
                        In = new WhereInCallExpressionParameter
                        {
                            ParameterName = parameterName
                        }
                    };

                    foreach (var item in fieldList)
                    {
                        result.In.ParameterValue.Add(item);
                        //if (whereBuilder.Length <= 0)
                        //{
                        //    whereBuilder.Append($"{sqlParameterName} IN ");
                        //    whereBuilder.Append("(");
                        //}

                        //var parameterValue = $"{ParameterManager.Instance.GetDatabaseParameterTag(context)}_in_{context.ParameterValues.Count}";
                        //whereBuilder.Append($"{parameterValue}, ");
                        //context.ParameterValues[parameterValue] = item;
                    }

                    //if (whereBuilder.Length > 0)
                    //    whereBuilder.Remove(whereBuilder.Length - 2, 2);

                    //if (whereBuilder.Length > 0)
                    //{
                    //    whereBuilder.Append(")");
                    //}
                    if (result.In.ParameterValue.Count == 0)
                    {
                        throw new MainApplicationException("In 条件里不存在任何数据");
                    }
                }
            }

            return result;
        }

        #endregion in

        #region not in

        private static IWhereCallExpressionDescriptor BuildNotIn(ExpressionContext context, MethodCallExpression callExpression)
        {
            if (callExpression.Arguments.Count < 2)
                throw new MainApplicationException("获取不到 NOT IN 函数信息");

            var memberExpression = ValueExpressionHandler.Handler(context, callExpression.Arguments[0]);

            if (memberExpression == null)
                throw new MainApplicationException("获取不到 NOT IN 函数");

            var parameterExpression = memberExpression.Expression as ParameterExpression;

            if (parameterExpression == null)
                throw new MainApplicationException("获取不到 NOT IN 函数的参数信息");

            WhereCallExpressionDescriptor result = null;

            var parameterName = $"{memberExpression.Value}";

            //// SQL 参数名。
            //var sqlParameterName = ParameterManager.Instance.GetParameterName(context, parameterExpression, $"{memberExpression.Value}");

            var valueResult = ValueExpressionHandler.Handler(context, callExpression.Arguments[1]);

            //var whereBuilder = new StringBuilder();

            if (valueResult.Status)
            {
                var fieldList = (IEnumerable)valueResult.Value;

                if (fieldList != null)
                {
                    result = new WhereCallExpressionDescriptor
                    {
                        NotIn = new WhereNotInCallExpressionParameter
                        {
                            ParameterName = parameterName
                        }
                    };

                    foreach (var item in fieldList)
                    {
                        result.NotIn.ParameterValue.Add(item);
                        //if (whereBuilder.Length <= 0)
                        //{
                        //    whereBuilder.Append($"{sqlParameterName} NOT IN ");
                        //    whereBuilder.Append("(");
                        //}

                        //var parameterValue = $"{ParameterManager.Instance.GetDatabaseParameterTag(context)}_in_{context.ParameterValues.Count}";
                        //whereBuilder.Append($"{parameterValue}, ");
                        //context.ParameterValues[parameterValue] = item;
                    }

                    //if (whereBuilder.Length > 0)
                    //    whereBuilder.Remove(whereBuilder.Length - 2, 2);

                    //if (whereBuilder.Length > 0)
                    //{
                    //    whereBuilder.Append(")");
                    //}
                    if (result.NotIn.ParameterValue.Count == 0)
                    {
                        throw new MainApplicationException("NOT IN 条件里不存在任何数据");
                    }
                }
            }

            return result;
        }

        #endregion not in

        #region between

        private static IWhereCallExpressionDescriptor BuildBetween(ExpressionContext context, MethodCallExpression callExpression)
        {
            if (callExpression.Arguments.Count < 3)
                throw new MainApplicationException("获取不到 Between 函数信息");

            var memberExpression = ValueExpressionHandler.Handler(context, callExpression.Arguments[0]);

            if (memberExpression == null)
                throw new MainApplicationException("获取不到 Between 函数");

            var parameterExpression = memberExpression.Expression as ParameterExpression;

            if (parameterExpression == null)
                throw new MainApplicationException("获取不到 Between 函数的参数信息");

            //// SQL 参数名。
            //var sqlParameterName = ParameterManager.Instance.GetParameterName(context, parameterExpression, $"{memberExpression.Value}");

            var startValue = ValueExpressionHandler.Handler(context, callExpression.Arguments[1]);
            var endValue = ValueExpressionHandler.Handler(context, callExpression.Arguments[2]);

            var result = new WhereCallExpressionDescriptor
            {
                Between = new WhereBetweenCallExpressionParameter
                {
                    ParameterName = $"{memberExpression.Value}",
                    StartValue = startValue.Value,
                    EndValue = endValue.Value,
                }
            };

            //var startParameterName = $"between_{context.ParameterValues.Count}";
            //var startParameterValue = $"{ParameterManager.Instance.GetDatabaseParameterTag(context)}{startParameterName}";
            //context.ParameterValues[startParameterName] = startValue.Value;

            //var endParameterName = $"between_{context.ParameterValues.Count}";
            //var endParameterValue = $"{ParameterManager.Instance.GetDatabaseParameterTag(context)}{endParameterName}";
            //context.ParameterValues[endParameterName] = endValue.Value;

            //var whereBuilder = new StringBuilder();

            //whereBuilder.Append($"{sqlParameterName} BETWEEN {startParameterValue} AND {endParameterValue}");

            return result;
        }

        #endregion between
    }
}